package com.pavlinic.mylisp.parser;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

import com.pavlinic.mylisp.Expression;
import com.pavlinic.mylisp.Function;
import com.pavlinic.mylisp.ListExpression;
import com.pavlinic.mylisp.NullExpression;
import com.pavlinic.mylisp.SymbolExpression;
import com.pavlinic.mylisp.ValueExpression;

public class Parser {
	private static final String LEFT_BRACKET = "(";
	private static final String RIGHT_BRACKET = ")";
	private static final String DOUBLE_QUOTE = "\"";
	private static final String FALSE = "false";
	private static final String TRUE = "true";
	private static final String QUOTE = "'";
	private static final String NULL = "null";
	
	private final Tokenizer tokenizer;

	public Parser(Tokenizer tokenizer) {
		this.tokenizer = tokenizer;
	}

	public Expression parse(String string) {
		final List<String> tokens = tokenizer.getTokens(string);

		return parseInternal(tokens);

	}

	private Expression parseInternal(List<String> tokens) {
		final String token = tokens.remove(0);

		if (token.equals(LEFT_BRACKET)) {
			return parseListInternal(tokens);
		} else if (token.equals(QUOTE)) {
			final ListExpression quoteExpression = new ListExpression();
			quoteExpression.add(atomicExpression("quote"));
			quoteExpression.add(parseInternal(tokens));
			return quoteExpression;
		}
		return atomicExpression(token);
	}

	private Expression atomicExpression(final String token) {
		if (token.equals(TRUE) || token.equals(FALSE)) {
			return new ValueExpression(Boolean.parseBoolean(token));
		} else if (token.equals(NULL)) {
			return NullExpression.INSTANCE;
		} else if (token.startsWith(DOUBLE_QUOTE) && token.endsWith(DOUBLE_QUOTE)) {
			return new ValueExpression(token.substring(1, token.length() - 1));
		}
		try {
			return new ValueExpression(new BigInteger(token));
		} catch (NumberFormatException e) {
			return new SymbolExpression(token);
		}
	}

	private Expression parseListInternal(List<String> tokens) {
		final ListExpression list = new ListExpression();

		while (!tokens.isEmpty()) {
			final String token = tokens.get(0);
			if (token.equals(RIGHT_BRACKET)) {
				tokens.remove(0);
				return list;
			} else {
				list.add(parseInternal(tokens));
			}
		}
		// Should only get here on invalid syntax
		throw new RuntimeException("Missing )");
	}

	public ListExpression parseList(String string) {
		return (ListExpression) parse(string);
	}

	public SymbolExpression parseAtom(String string) {
		return (SymbolExpression) parse(string);
	}

	public List<Expression> parseMultipleExpressions(String string) {
		final List<String> tokens = tokenizer.getTokens(string);
		final List<Expression> returned = new ArrayList<Expression>();
		while (!tokens.isEmpty()) {
			returned.add(parseInternal(tokens));
		}
		return returned;
	}

	public Function parseFunction(String expression) {
		return (Function) parse(expression);
	}
}
